﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

using XmlVisualizer.DiagramDesigner.Controls;
using XmlVisualizer.Models;
using XmlVisualizer.Exceptions;

namespace XmlVisualizer.DiagramDesigner {

	/// <summary>
	/// Static class with strings representing DesignerItem parts. 
	/// </summary>
	public static class DesignerItemParts {
		/// <summary>
		/// DragThumb part's name.
		/// </summary>
		public const String PART_DragThumb = "PART_DragThumb";
		/// <summary>
		/// ResizeDecorator part's name.
		/// </summary>
		public const String PART_ResizeDecorator = "PART_ResizeDecorator";
		/// <summary>
		/// Connector decorator part's name.
		/// </summary>
		public const String PART_ConnectorDecorator = "PART_ConnectorDecorator";
		/// <summary>
		/// Appearance part's name.
		/// </summary>
		public const String PART_Appearance = "PART_Appearance";
		/// <summary>
		/// ContentPresenter parts's name.
		/// </summary>
		public const String PART_ContentPresenter = "PART_ContentPresenter";
	}

	//These attributes identify the types of the named parts that are used for templating
	/// <summary>
	/// DesignerItem represents a container for some content. This container can be moved, connected with others etc.
	/// </summary>
	[TemplatePart(Name = DesignerItemParts.PART_DragThumb, Type = typeof(DragThumb))]
	[TemplatePart(Name = DesignerItemParts.PART_ResizeDecorator, Type = typeof(Control))]
	[TemplatePart(Name = DesignerItemParts.PART_ConnectorDecorator, Type = typeof(Control))]
	[TemplatePart(Name = DesignerItemParts.PART_Appearance, Type = typeof(Control))]
	[TemplatePart(Name = DesignerItemParts.PART_ContentPresenter, Type = typeof(ContentPresenter))]
	public partial class DesignerItem : ContentControl, ISelectable, IGroupable {
		#region Static fields
		public static String AppearanceDefaultTemplateName = "AppearanceProcess";
		#endregion

		#region Helper methods
		public DragThumb GetDragThumb() {
			return this.Template.FindName(DesignerItemParts.PART_DragThumb, this) as DragThumb;
		}

		public Control GetResizeDecorator() {
			return this.Template.FindName(DesignerItemParts.PART_ResizeDecorator, this) as Control;
		}

		public Control GetConnectorDecorator() {
			return this.Template.FindName(DesignerItemParts.PART_ConnectorDecorator, this) as Control;
		}

		public Control GetAppearance() {
			return this.Template.FindName(DesignerItemParts.PART_Appearance, this) as Control;
		}

		public ContentPresenter GetContentPresenter() {
			return this.Template.FindName(DesignerItemParts.PART_ContentPresenter, this) as ContentPresenter;
		}

		public Connector GetConnector(Connectors connector) {
			Control childConnectorDecorator = GetConnectorDecorator();
			childConnectorDecorator.ApplyTemplate();
			return childConnectorDecorator.Template.FindName(connector.ToString(), childConnectorDecorator) as Connector;
		}
		#endregion

		#region Dependency properties
		#region NodeProperty
		public XNode Node {
			get { return (XNode)GetValue(NodeProperty); }
			set { SetValue(NodeProperty, value); }
		}

		public static DependencyProperty NodeProperty = DependencyProperty.Register("Node", typeof(XNode), typeof(DesignerItem));
		#endregion

		#region ElementProperty
		/// <summary>
		/// Gets or sets the ElementProperty.
		/// </summary>
		public XElement Element {
			get { return (XElement)GetValue(ElementProperty); }
			set { SetValue(ElementProperty, value); }
		}
		/// <summary>
		/// Identifies the XmlVisualizer.DiagramDesigner.DesignerItem.ElementProperty.
		/// </summary>
		public static DependencyProperty ElementProperty = DependencyProperty.Register("Element", typeof(XElement), typeof(DesignerItem));
		#endregion

		#region ElementNameProperty
		/// <summary>
		/// Gets or sets the ElementNameProperty.
		/// </summary>
		public String ElementName {
			get { return (String)GetValue(ElementNameProperty); }
			set { SetValue(ElementNameProperty, value); }
		}
		/// <summary>
		/// Identifies the XmlVisualizer.DiagramDesigner.DesignerItem.ElementNameProperty.
		/// </summary>
		public static readonly DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName", typeof(String), typeof(DesignerItem));
		#endregion

		#region ParentIDProperty
		/// <summary>
		/// Gets or sets the ParentIDProperty.
		/// </summary>
		public Guid ParentID {
			get { return (Guid)GetValue(ParentIDProperty); }
			set { SetValue(ParentIDProperty, value); }
		}
		/// <summary>
		/// Identifies the XmlVisualizer.DiagramDesigner.DesignerItem.ParentIDProperty.
		/// </summary>
		public static readonly DependencyProperty ParentIDProperty = DependencyProperty.Register("ParentID", typeof(Guid), typeof(DesignerItem));
		#endregion

		#region IsGroupProperty
		/// <summary>
		/// Gets or sets the IsGroupProperty.
		/// </summary>
		public bool IsGroup {
			get { return (bool)GetValue(IsGroupProperty); }
			set { SetValue(IsGroupProperty, value); }
		}
		/// <summary>
		/// Identifies the XmlVisualizer.DiagramDesigner.DesignerItem.IsGroupProperty.
		/// </summary>
		public static readonly DependencyProperty IsGroupProperty =
			DependencyProperty.Register("IsGroup", typeof(bool), typeof(DesignerItem));
		#endregion

		#region IsSelectedProperty
		/// <summary>
		/// Gets or sets the IsSelectedProperty.
		/// </summary>
		public bool IsSelected {
			get { return (bool)GetValue(IsSelectedProperty); }
			set { SetValue(IsSelectedProperty, value); }
		}
		/// <summary>
		/// Identifies the XmlVisualizer.DiagramDesigner.DesignerItem.IsSelectedProperty.
		/// </summary>
		public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(DesignerItem),
			new FrameworkPropertyMetadata(false, IsSelectedProperty_PropertyChanged));

		private static void IsSelectedProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
			if (o is DesignerItem) {
				DesignerItem item = o as DesignerItem;

				if ((bool)e.NewValue == true) {
					item.OldFillBrush = item.FillBrush;
					if (item.FillBrush is SolidColorBrush) {
						SolidColorBrush brush = item.FillBrush as SolidColorBrush;
						Color color = Color.Multiply(brush.Color, 1.8f);
						SetFillBrushProperty((UIElement)o, new SolidColorBrush(color));
					}
				} else {
					if (item.OldFillBrush != null)
						SetFillBrushProperty((UIElement)o, item.OldFillBrush);
				}
			}
		}
		#endregion

		#region AppearanceTemplateProperty
		/// <summary>
		/// Gets or sets the AppearancaTemplateProperty.
		/// </summary>
		public ControlTemplate AppearanceTemplate {
			get { return (ControlTemplate)GetValue(AppearanceTemplateProperty); }
			set { SetValue(AppearanceTemplateProperty, value); }
		}
		/// <summary>
		/// Identifies the XmlVisualizer.DiagramDesigner.DesignerItem.AppearanceTemplateProperty.
		/// </summary>
		public static readonly DependencyProperty AppearanceTemplateProperty =
			DependencyProperty.RegisterAttached("AppearanceTemplate", typeof(ControlTemplate), typeof(DesignerItem));
		#endregion

		#region DragThumbTemplateProperty
		/// <summary>
		/// Gets or sets the DragThumbTemplateProperty.
		/// </summary>
		public ControlTemplate DragThumbTemplate {
			get { return (ControlTemplate)GetValue(DragThumbTemplateProperty); }
			set { SetValue(DragThumbTemplateProperty, value); }
		}

		/// <summary>
		/// Identifies the XmlVisualizer.DiagramDesigner.DesignerItem.DragThumbTemplateProperty.
		/// </summary>
		public static readonly DependencyProperty DragThumbTemplateProperty =
			DependencyProperty.RegisterAttached("DragThumbTemplate", typeof(ControlTemplate), typeof(DesignerItem));

		/// <summary>
		/// Gets the drag thumb template for specified UIElement.
		/// </summary>
		/// <param name="element">UIElement to get the ControlTemplate of</param>
		/// <returns>UIElement's ControlTemplate.</returns>
		/// <exception cref="System.ArgumentNullException">element is null.</exception>
		public static ControlTemplate GetDragThumbTemplate(UIElement element) {
			if (element == null)
				throw new ArgumentNullException("element");
			return (ControlTemplate)element.GetValue(DragThumbTemplateProperty);
		}

		/// <summary>
		/// Sets the drag thumb template for the specified UIElement.
		/// </summary>
		/// <param name="element">UIElement which template is being set.</param>
		/// <param name="value">ControlTemplate to set.</param>
		/// <exception cref="System.ArgumentNullException">element is null.</exception>
		public static void SetDragThumbTemplate(UIElement element, ControlTemplate value) {
			if (element == null)
				throw new ArgumentNullException("element");
			element.SetValue(DragThumbTemplateProperty, value);
		}
		#endregion

		#region ConnectorDecoratorTemplateProperty
		/// <summary>
		/// Gets or sets the ConnectorDecoratorTemplateProperty.
		/// </summary>
		public ControlTemplate ConnectorDecoratorTemplate {
			get { return (ControlTemplate)GetValue(ConnectorDecoratorTemplateProperty); }
			set { SetValue(ConnectorDecoratorTemplateProperty, value); }
		}

		/// <summary>
		/// Identifies the ConnectorDecoratorTemplateProperty.
		/// </summary>
		public static readonly DependencyProperty ConnectorDecoratorTemplateProperty =
			DependencyProperty.RegisterAttached("ConnectorDecoratorTemplate", typeof(ControlTemplate), typeof(DesignerItem));

		/// <summary>
		/// Gets the connector decorator's template.
		/// </summary>
		/// <param name="element">UIElement to get the template from.</param>
		/// <returns>ConnectorDecoratorTemplate of the specified UIElement.</returns>
		/// <exception cref="System.ArgumentNullException">element is null.</exception>
		public static ControlTemplate GetConnectorDecoratorTemplate(UIElement element) {
			if (element == null)
				throw new ArgumentNullException("element");
			return (ControlTemplate)element.GetValue(ConnectorDecoratorTemplateProperty);
		}

		/// <summary>
		/// Set the connector decorator's template.
		/// </summary>
		/// <param name="element">UIElement to set the template on.</param>
		/// <param name="value">ControlTemplate to set.</param>
		/// <exception cref="System.ArgumentNullException">element is null.</exception>
		public static void SetConnectorDecoratorTemplate(UIElement element, ControlTemplate value) {
			if (element == null)
				throw new ArgumentNullException("element");
			element.SetValue(ConnectorDecoratorTemplateProperty, value);
		}
		#endregion

		#region IsDragConnectionOverProperty
		// while drag connection procedure is ongoing and the mouse moves over 
		// this item this value is true; if true the ConnectorDecorator is triggered
		// to be visible, see template
		/// <summary>
		/// Gets or sets the IsDragConnectionOverProperty.
		/// </summary>
		public bool IsDragConnectionOver {
			get { return (bool)GetValue(IsDragConnectionOverProperty); }
			set { SetValue(IsDragConnectionOverProperty, value); }
		}

		/// <summary>
		/// Identifies the IsDragConnectionOverProperty.
		/// </summary>
		public static readonly DependencyProperty IsDragConnectionOverProperty =
			DependencyProperty.Register("IsDragConnectionOver", typeof(bool), typeof(DesignerItem), new FrameworkPropertyMetadata(false));
		#endregion

		#region FillBrushProperty
		public static void SetFillBrushProperty(UIElement element, Brush value) {
			if (element != null) {
				element.SetValue(FillBrushProperty, value);
			}
		}

		public Brush FillBrush {
			get { return (Brush)GetValue(FillBrushProperty); }
			set { SetValue(FillBrushProperty, value); }
		}

		public static readonly DependencyProperty FillBrushProperty =
			DependencyProperty.Register("FillBrush", typeof(Brush), typeof(DesignerItem),
				new FrameworkPropertyMetadata(new SolidColorBrush(Properties.Settings.Default.defaultFillColor)));
		#endregion

		#region StrokeBrushProperty
		public static void SetStrokeBrushProperty(UIElement element, Brush value) {
			if (element != null)
				element.SetValue(StrokeBrushProperty, value);
		}

		public Brush StrokeBrush {
			get { return (Brush)GetValue(StrokeBrushProperty); }
			set { SetValue(StrokeBrushProperty, value); }
		}

		public static readonly DependencyProperty StrokeBrushProperty =
			DependencyProperty.Register("StrokeBrush", typeof(Brush), typeof(DesignerItem),
				new FrameworkPropertyMetadata(new SolidColorBrush(Properties.Settings.Default.defaultStrokeColor)));
		#endregion
		#endregion

		#region Properties
		private Brush oldFillBrush;
		public Brush OldFillBrush {
			get { return oldFillBrush; }
			set { oldFillBrush = value; }
		}
		
		private Guid id;
		public Guid ID {
			get { return id; }
		}

		private XNodeInfo info;
		public XNodeInfo Info {
			get { return info; }
			set { info = value; }
		}
		#endregion

		#region Constructors
		/// <summary>
		/// Initializes DesignerItem class.
		/// </summary>
		static DesignerItem() {
			// set the key to reference the style for this control
			FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(DesignerItem), new FrameworkPropertyMetadata(typeof(DesignerItem)));
		}

		/// <summary>
		/// Initializes a new instance of the DesignerItem class. Sets Guid.
		/// </summary>
		/// <param name="id">Guid to set.</param>
		/// <exception cref="System.ArgumentNullException">id is null.</exception>
		public DesignerItem(Guid id) {
			if (id == null)
				throw new ArgumentNullException("id");
			this.id = id;
			this.Loaded += new RoutedEventHandler(DesignerItem_Loaded);
		}

		/// <summary>
		/// Initializes a new instance of the DesignerItem class. Sets Guid, AppearanceTemplate, AppeareanceName and XNodeInfo.
		/// </summary>
		/// <param name="id">Guid to set.</param>
		/// <param name="appearanceTemplate">ControlTemplate which represents appearance of this item.</param>
		/// <param name="info">XNodeInfo with information about underlying XNode and much more.</param>
		public DesignerItem(Guid id, ControlTemplate appearanceTemplate, XNodeInfo info)
			: this(id) {

			this.Info = info;

			if (appearanceTemplate != null)
				AppearanceTemplate = appearanceTemplate;
			else
				throw new ArgumentNullException("appearanceTemplate");

			this.ApplyTemplate();
		}

		/// <summary>
		/// Initializes a new instance of the DesignerItem class. Default constructor.
		/// </summary>
		public DesignerItem()
			: this(Guid.NewGuid()) {
		}

		/// <summary>
		/// Initializes a new instance of the DesignerItem class. Set the appearance and connector decorator templates.
		/// </summary>
		/// <param name="appearanceTemplate">ControlTemplate to set as a appearance template.</param>
		/// <param name="appearanceName">Name of the ControlTemplate representing appearance.</param>
		/// <param name="info">XNodeInfo with information about underlying XNode and much more.</param>
		/// <exception cref="System.ArgumentNullException">appearanceTemplate or connectorDecoratorTemplate is null.</exception>
		public DesignerItem(ControlTemplate appearanceTemplate, XNodeInfo info)
			: this(Guid.NewGuid()) {

			this.Info = info;

			if (appearanceTemplate != null)
				AppearanceTemplate = appearanceTemplate;
			else
				throw new ArgumentNullException("appearanceTemplate");

			this.ApplyTemplate();
		}
		#endregion

		#region Event handlers
		/// <summary>
		/// Invoked when an unhandled routed event reaches this element. 
		/// </summary>
		/// <param name="e">The event data.</param>
		protected override void OnPreviewMouseDown(MouseButtonEventArgs e) {
			base.OnPreviewMouseDown(e);
			DesignerCanvas designerCanvas = VisualTreeHelper.GetParent(this) as DesignerCanvas;

			// update selection
			if (designerCanvas != null) {
				if ((Keyboard.Modifiers & (ModifierKeys.Shift | ModifierKeys.Control)) != ModifierKeys.None)
					if (this.IsSelected) {
						designerCanvas.selectionService.RemoveFromSelection(this);
					} else {
						designerCanvas.selectionService.AddToSelection(this);
					} else if (!this.IsSelected) {
					designerCanvas.selectionService.SelectItem(this);
				}
				//Focus();
			}

			e.Handled = false;
		}

		/// <summary>
		/// Invoked when an unhandled routed event reaches this element.
		/// </summary>
		/// <param name="e">The event data.</param>
		protected override void OnMouseDown(MouseButtonEventArgs e) {
			base.OnMouseDown(e);
			DesignerCanvas designer = VisualTreeHelper.GetParent(this) as DesignerCanvas;
			if (designer != null) {
				Focus();
			}
		}

		private void DesignerItem_Loaded(object sender, RoutedEventArgs e) {
			if (base.Template != null) {
				ContentPresenter contentPresenter = this.Template.FindName(DesignerItemParts.PART_ContentPresenter, this) as ContentPresenter;
				if (contentPresenter != null) {
					UIElement contentVisual = contentPresenter.Content as UIElement;
					if (contentVisual != null) {
						DragThumb thumb = GetDragThumb();
						if (thumb != null) {
							ControlTemplate template = DesignerItem.GetDragThumbTemplate(contentVisual) as ControlTemplate;
							if (template != null)
								thumb.Template = template;
						}
					}
				}
			}
		}

		protected override void OnRender(DrawingContext drawingContext) {
			base.OnRender(drawingContext);
		}
		#endregion

		#region Methods
		/// <summary>
		/// Calls Controller to connect two nodes.
		/// </summary>
		/// <param name="sourceConnector">source Connector</param>
		/// <param name="sinkConnector">sink Connector</param>
		/// <returns>ConnectResult with the result of connection.</returns>
		public ConnectResult TryConnect(Connector sourceConnector, Connector sinkConnector) {
			return Document.CurrentController.ConnectNodes(sourceConnector.ParentDesignerItem.Node, sinkConnector.ParentDesignerItem.Node);
		}
		#endregion
	}

	/// <summary>
	/// Enumeration with possible Connector names.
	/// </summary>
	public enum Connectors {
		Top,
		Bottom,
		Left,
		Right
	}

	/// <summary>
	/// Converts string to 0 or 1. 0 if string is empty, 1 otherwise.
	/// </summary>
	public class EmptyConverter : IValueConverter {

		#region IValueConverter Members
		/// <summary>
		/// Converts string to "0" or "1".
		/// </summary>
		/// <param name="value">object to convert</param>
		/// <param name="targetType">target type</param>
		/// <param name="parameter">conversion parameter</param>
		/// <param name="culture">CultureInfo for conversion</param>
		/// <returns>Converted value.</returns>
		public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
			if (String.IsNullOrEmpty((String)value)) {
				return "0";
			} else {
				return "1";
			}
		}

		/// <summary>
		/// Returns null.
		/// </summary>
		/// <param name="value"></param>
		/// <param name="targetType"></param>
		/// <param name="parameter"></param>
		/// <param name="culture"></param>
		/// <returns>null.</returns>
		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
			return null;
		}

		#endregion
	}

	public class FillBrushConverter : IValueConverter {

		#region IValueConverter Members

		public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
			if (value == null) {
				return Properties.Settings.Default.defaultFillColor;
			} else {
				return value;
			}
		}

		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
			throw new NotImplementedException();
		}

		#endregion
	}
}
